package top.hmtools.services;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import top.hmtools.base.CharsetTools;
import top.hmtools.base.StringTools;
import top.hmtools.base.ThreadLocalTools;
import top.hmtools.beans.DBConnectInfoBean;
import top.hmtools.beans.MapperXmlFileTempletBean;
import top.hmtools.beans.MapperXmlSqlPartGroupBean;
import top.hmtools.beans.MapperXmlSqlPartTempletBean;
import top.hmtools.beans.columnsInfoBeans.ColumnsBaseInfoBean;
import top.hmtools.collection.ListTools;
import top.hmtools.conversion.DataTypeMysql2Java;
import top.hmtools.dataFormat.DateFormatTools;
import top.hmtools.dataFormat.XmlFromatTools;
import top.hmtools.enums.EDBType;
import top.hmtools.enums.EDateFormat;
import top.hmtools.enums.ESqlPartFactor;
import top.hmtools.io.IoFileTools;
import top.hmtools.io.IoTools;
import top.hmtools.model.ResultModel;
import top.hmtools.model.ResultModel.resultStatus;
import top.hmtools.system.SystemInfoTools;

/**
 * 根据数据库表数据，生成对应mybatis可用的pojo，dao，mapper.xml
 * @author HyboJ
 *
 */
@Service
public class GenerateService extends BaseService{
	
	@Autowired
	private DbManageService dbManageService;
	
	/**
	 * 表名称
	 */
	private String tableName;
	
	/**
	 * 存储路径
	 */
	private String savePath;
	
	private String pojoPackage;
	
	private String pojoName;
	
	private String daoPackage;
	
	private String daoName;
	
	private String mapperXmlPackage;
	
	private String mapperXmlName;
	
	private boolean isPojoCreatComment;
	
	/**
	 * 是否使用驼峰格式化变量
	 */
	private boolean isCamelVar  ;
	
	private boolean isOverrwiterToString;
	
	private boolean isDaoCreateComment;
	
	private EDBType dbType;
	
	private MapperXmlFileTempletBean xmlFileTempletBean;
	
	private Charset charset;
	
	private DBConnectInfoBean dbconn  ;
	
	/**
	 * 生成实体类
	 * @return
	 */
	public ResultModel<String> generatePojo(){
		ResultModel<String> result = new ResultModel<>(resultStatus.STATUS_FAIL);
		if(this.dbconn ==null){
			result.setMessage("请选择数据库并建立链接。。");
			return result;
		}
		if(StringTools.isBlank(tableName)){
			result.setMessage("请选择要进行操作的数据库表格。。");
			return result;
		}
		
		StringBuffer sbContent = new StringBuffer();
		
		sbContent.append("package "+this.pojoPackage+";");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("import java.io.Serializable;");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		
		sbContent.append("/**");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("*对应的数据库表名为：" + this.tableName);
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("*<br>作者为：" + SystemInfoTools.getUserName());
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("*<br>创建时间为：" + DateFormatTools.getFormatDatetime(new Date(), EDateFormat.yyyyMMdd_nyr_HHmmss_sfm));
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("*/");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("public class "+this.pojoName+" implements Serializable{");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		
		
		
		//获取所有表字段信息
		ResultModel<ColumnsBaseInfoBean> columns = this.dbManageService.getColumns(this.dbconn, tableName);
		
		// 生成字段
		for (ColumnsBaseInfoBean column : columns.getRows()) {
			String columnName = "";
			if (isCamelVar) {
				columnName = StringTools.underlineToCamelByRegex(column.getColumnName().toLowerCase());
			} else {
				columnName = column.getColumnName().toLowerCase();
			}
			sbContent.append("/**");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*" + column.getColumnComment());
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*/");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("protected  "+DataTypeMysql2Java.getFullJavaName(column.getDataType())+"  " + columnName + ";");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append(SystemInfoTools.getOSLineSeparator());
		}
		
		//生成 getter setter
		for(ColumnsBaseInfoBean column:columns.getRows()){
			String beanName = "";
			if(isCamelVar){
				beanName = StringTools.underlineToCamelByRegex(column.getColumnName().toLowerCase());
			}else{
				beanName = column.getColumnName().toLowerCase();
			}
			sbContent.append("/**");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*获取"+column.getColumnComment());
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*/");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("public "+DataTypeMysql2Java.getFullJavaName(column.getDataType())+" get"+StringTools.upcaseFirst(beanName)+"(){");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("return this."+beanName+";");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("}");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append(SystemInfoTools.getOSLineSeparator());

			sbContent.append("/**");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*设置"+column.getColumnComment());
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("*/");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("public void set"+StringTools.upcaseFirst(beanName)+"("+DataTypeMysql2Java.getFullJavaName(column.getDataType())+" "+beanName+"){");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("this."+beanName+"="+beanName+";");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
			sbContent.append("}");
			sbContent.append(SystemInfoTools.getOSLineSeparator());
		}
		
		
		
		sbContent.append("}");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		
		result.setStatusSuccess();
		result.setMessage(sbContent.toString());
		return result;
	}

	public ResultModel<String> generateDao() {
		ResultModel<String> result = new ResultModel<>(resultStatus.STATUS_FAIL);
		StringBuffer sbContent = new StringBuffer();
		result.setMessage(sbContent.toString());
		
		sbContent.append("package  "+this.daoPackage+";");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("import java.util.List;");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("import org.apache.ibatis.annotations.Param;");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("import "+this.pojoPackage+"."+this.pojoName+";");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		
		
		sbContent.append("public interface  "+this.daoName+"{");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("");
		sbContent.append("}");
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		sbContent.append(SystemInfoTools.getOSLineSeparator());
		
		result.setStatusSuccess();
		result.setMessage(sbContent.toString());
		return result;
	}
	
	/**
	 * 生成mybatis 的mapper.xml文件内容
	 * @return
	 */
	public ResultModel<String> generateMapperXml() {
		ResultModel<String> result = new ResultModel<String>(resultStatus.STATUS_FAIL);
		
		//获取模板内容
		if(this.xmlFileTempletBean == null){
			result.setMessage("请选择模板");
			return result;
		}
		String templetContent = this.xmlFileTempletBean.getContent();
		if(StringTools.isBlank(templetContent)){
			result.setMessage("模板内容为空");
			return result;
		}
		
		//获取所有因子值，并添加到数据模型
	    Map<String,String> dataModels = new HashMap<String,String>();
	    if(this.dbconn == null){
	    	result.setMessage("未指定要连接的数据库");
	    	return result;
	    }
	    
	    List<String> factorsInTemplet = this.getAllFactorsInTemplet(templetContent);
	    if(factorsInTemplet.size()<1){
	    	result.setMessage(templetContent);
	    	return result;
	    }
	    //分组外的独立SQL片断
	    List<MapperXmlSqlPartTempletBean> sqlParts = this.mapperXmlSqlPartTempletDao.getSqlPartTempletsByCondition2(this.dbconn.getDb_type(),factorsInTemplet,factorsInTemplet);
	    if(sqlParts != null && sqlParts.size()>0){
	    	for(MapperXmlSqlPartTempletBean bean:sqlParts){
	    		dataModels.put(bean.getFactor_name(), this.generateSqlPart(bean));
	    	}
	    }
	    
	    //分组后的打包SQL片断
	    List<MapperXmlSqlPartGroupBean> groupBeans = this.mapperXmlSqlPartGroupDao.getSqlPartGroupByDbTypeFactors(factorsInTemplet);
	    if(groupBeans !=null && groupBeans.size()>0){
	    	for(MapperXmlSqlPartGroupBean bean:groupBeans){
	    		dataModels.put(bean.getFactor_name(), this.generateSqlPart(bean));
	    	}
	    }
	
	    //合并模板与数据模型
	    InputStreamReader isrContent = new InputStreamReader(new ByteArrayInputStream(templetContent.getBytes()));
	    Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
	    StringWriter stringWriter = new StringWriter();
	    Template temp = null;
		try {
			temp = new Template("name1", isrContent,cfg);
			temp.process(dataModels, stringWriter);
			result.setStatusSuccess();
//			result.setMessage((XmlFromatTools.outPutStr(stringWriter.toString(), this.charset.name())));
			result.setMessage(stringWriter.toString());
		} catch (IOException | TemplateException e) {
			e.printStackTrace();
			logger.error("生成mapper.xml失败："+e.getMessage(),e);
			result.setMessage("生成mapper.xml失败："+e.getMessage());
		}
		return result;
	}

	/**
	 * 从模板文本内容中提取出所有的因子（占位符）
	 * @param templetContent
	 * @return
	 */
	private List<String> getAllFactorsInTemplet(String templetContent){
		List<String> allFactors = StringTools.distillRegex(templetContent,"\\$\\{[-,_\\w]+!?\\w*\\}");
		allFactors = ListTools.removeDuplicate(allFactors);
		List<String> result = new ArrayList<>();
		for(String str:allFactors){
			String tmp = str.replaceAll("\\$\\{([-,_\\w]+)!?\\w*\\}", "$1");
			result.add(tmp);
		}
		return result;
	}
	
	/**
	 * 合成SQL语句片段
	 * @param bean
	 * @return
	 */
	private String generateSqlPart(MapperXmlSqlPartTempletBean bean){
		//获取模板内容
		String templetContent = bean.getContent();
		if(StringTools.isAllBlank(templetContent)){
			return null;
		}
		InputStreamReader isrContent = new InputStreamReader(new ByteArrayInputStream(templetContent.getBytes()));
		
		//获取所有因子值，并添加到数据模型
		ResultModel<ColumnsBaseInfoBean> columns = this.dbManageService.getColumns(this.dbconn, this.tableName);
		
		Map<String,Object> dataModels = new HashMap<>();
		dataModels.put(ESqlPartFactor.methodName.toString(),bean.getMethod_name() );
		dataModels.put(ESqlPartFactor.pojoFullName.toString(), StringTools.trimSuffix(this.pojoPackage, ".")+"."+this.pojoName);
		dataModels.put(ESqlPartFactor.daoFullName.toString(), StringTools.trimSuffix(this.daoPackage, ".")+"."+this.daoName);
		dataModels.put(ESqlPartFactor.tableName.toString(), this.tableName);
		for(ColumnsBaseInfoBean item:columns.getRows()){
			String aliasName = null;
			//设置字段别名
			if(this.isCamelVar){
				aliasName = StringTools.underlineToCamelByRegex(item.getColumnName().toLowerCase());
			} else {
				aliasName = item.getColumnName();
			}
			item.setAliasName(aliasName);
			
			//设置主键
			if(item.isPriKey() && dataModels.get(ESqlPartFactor.priKey.toString())==null){
				dataModels.put(ESqlPartFactor.priKey.toString(), item.getColumnName());
			}
		}
		dataModels.put(ESqlPartFactor.columns.toString(), columns.getRows());
		
		//合并模板与数据模型
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
        StringWriter stringWriter = new StringWriter();
        Template temp = null;
		try {
			temp = new Template("name1", isrContent,cfg);
			temp.process(dataModels, stringWriter);
		} catch (IOException | TemplateException e) {
			logger.error("生成mapper.xml失败："+e.getMessage(),e);
			return e.getMessage();
		}
		return stringWriter.toString();
	}

	/**
	 * 获取分组的SQL语句片段
	 * @param bean
	 * @return
	 */
	private String generateSqlPart(MapperXmlSqlPartGroupBean bean) {
		List<MapperXmlSqlPartTempletBean> sqlParts = this.mapperXmlSqlPartTempletDao.getSqlPartTempletsByCondition(this.dbconn.getDb_type(), bean.getId());
		StringBuffer result = new StringBuffer();
		for(MapperXmlSqlPartTempletBean tmp:sqlParts){
			result.append(this.generateSqlPart(tmp));
			result.append(SystemInfoTools.getOSLineSeparator());
			result.append(SystemInfoTools.getOSLineSeparator());
		}
		return result.toString();
	}

	public void init() {
		this.tableName = this.getTableName();
		this.savePath = ThreadLocalTools.get("tf_savePath", TextField.class).getText();
		this.pojoPackage = ThreadLocalTools.get("tf_pojoPackage", TextField.class).getText();
		this.pojoPackage = StringTools.trimSuffix(this.pojoPackage, ".");
		this.pojoName = ThreadLocalTools.get("tf_pojoName", TextField.class).getText();
		this.daoPackage = ThreadLocalTools.get("tf_daoPackage", TextField.class).getText();
		this.daoPackage = StringTools.trimSuffix(this.daoPackage, ".");
		
		this.daoName = ThreadLocalTools.get("tf_daoName", TextField.class).getText();
		this.mapperXmlPackage = ThreadLocalTools.get("tf_mapperXmlPackage", TextField.class).getText();
		this.mapperXmlName = ThreadLocalTools.get("tf_mapperXmlName", TextField.class).getText();
		
		this.isPojoCreatComment = ThreadLocalTools.get("cb_isPojoCreatComment", CheckBox.class).selectedProperty().get();
		this.isCamelVar = this.getIsUseCamelName();
		this.isOverrwiterToString = ThreadLocalTools.get("cb_isOverrwiterToString", CheckBox.class).selectedProperty().get();
		this.isDaoCreateComment = ThreadLocalTools.get("cb_isDaoCreateComment", CheckBox.class).selectedProperty().get();
		
		this.dbconn = this.getDBConnInfo();
		
		String dbTypeName = (String) ThreadLocalTools.get("cb_dbType", ComboBox.class).getValue();
		if(StringTools.isNotBlank(dbTypeName)){
			this.dbType = EDBType.getByTypeCode(EDBType.getTypeCodeByName(dbTypeName));
		}
		
		try {
			ComboBox db_mapperXmlTemplet = ThreadLocalTools.get("cb_mapperXmlTemplet", ComboBox.class);
			int mapperXmlTempletIndex = db_mapperXmlTemplet.getSelectionModel().getSelectedIndex();
			List<MapperXmlFileTempletBean> mapperXmlFileTemps = (List<MapperXmlFileTempletBean>) db_mapperXmlTemplet.getUserData();
			this.xmlFileTempletBean = mapperXmlFileTemps.get(mapperXmlTempletIndex);
			this.xmlFileTempletBean = mapperXmlFileTempletDao.getOneById(this.xmlFileTempletBean.getId());
		} catch (Exception e) {
			// skip
		}
		
		try {
			ComboBox cbCharset = ThreadLocalTools.get("cb_charset", ComboBox.class);
			String charsetStr = (String) cbCharset.getValue();
			this.charset = CharsetTools.toCharset(charsetStr);
		} catch (Exception e) {
			// skip
		}
		
	}

	private DBConnectInfoBean getDBConnInfo() {
		return ThreadLocalTools.get("dbConnInfo", DBConnectInfoBean.class);
	}

	/**
	 * 获取表名称
	 * @return
	 */
	private String getTableName() {
		return ThreadLocalTools.get("tf_tableName", TextField.class).getText();
	}

	/**
	 * 获取是否使用驼峰格式化变量
	 * @return
	 */
	private boolean getIsUseCamelName() {
		return ThreadLocalTools.get("cb_isCamelVar", CheckBox.class).selectedProperty().get();
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "GenerateService [tableName=" + tableName + ", savePath=" + savePath + ", pojoPackage=" + pojoPackage
				+ ", pojoName=" + pojoName + ", daoPackage=" + daoPackage + ", daoName=" + daoName
				+ ", mapperXmlPackage=" + mapperXmlPackage + ", mapperXmlName=" + mapperXmlName
				+ ", isPojoCreatComment=" + isPojoCreatComment + ", isCamelVar=" + isCamelVar
				+ ", isOverrwiterToString=" + isOverrwiterToString + ", isDaoCreateComment=" + isDaoCreateComment
				+ ", dbType=" + dbType + ", xmlFileTempletBean=" + xmlFileTempletBean + ", charset=" + charset
				+ ", dbconn=" + dbconn + "]";
	}

	/**
	 * 执行一键生成
	 * @return
	 */
	public ResultModel<String> doOneKeyGenerate() {
		ResultModel<String> result = new ResultModel<String>();
		StringBuffer sb = new StringBuffer();
		ResultModel<String> generatePojo = this.generatePojo();
		if(generatePojo.isStatusSuccess()){
			File file = new File(this.savePath+File.separator);
			if(!file.exists()){
				file.mkdirs();
			}
			file = new File(this.savePath+File.separator+this.pojoName+".java");
			OutputStream output = null;
			try {
				output = new FileOutputStream(file);
				IoTools.write(generatePojo.getMessage(), output, this.charset);
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				if(output != null){
					try {
						output.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			logger.info(file.getPath());
			sb.append(this.pojoName+"生成成功！");
		}else{
			sb.append(this.pojoName+"生成失败！");
		}
		
		ResultModel<String> generateMapperXml = this.generateMapperXml();
		if(generateMapperXml.isStatusSuccess()){
			File file = new File(this.savePath+File.separator);
			if(!file.exists()){
				file.mkdirs();
			}
			file = new File(this.savePath+File.separator+this.mapperXmlName+".xml");
			OutputStream output = null;
			try {
				output = new FileOutputStream(file);
				IoTools.write(generateMapperXml.getMessage(), output, this.charset);
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				if(output != null){
					try {
						output.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			logger.info(file.getPath());
			sb.append(this.mapperXmlName+"生成成功！");
		}else{
			sb.append(this.mapperXmlName+"生成失败！");
		}
		
		ResultModel<String> generateDao = this.generateDao();
		if(generateDao.isStatusSuccess()){
			File file = new File(this.savePath+File.separator);
			if(!file.exists()){
				file.mkdirs();
			}
			file = new File(this.savePath+File.separator+this.daoName+".java");
			OutputStream output = null;
			try {
				output = new FileOutputStream(file);
				IoTools.write(generateDao.getMessage(), output, this.charset);
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				if(output != null){
					try {
						output.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			logger.info(file.getPath());
			sb.append(this.daoName+"生成成功！");
		}else{
			sb.append(this.daoName+"生成失败！");
		}
		
		result.setMessage(sb.toString());
		result.setStatusSuccess();
		return result;
	}

	
}
